#ifndef __LLDEBUG_LUAINFO_H__
#define __LLDEBUG_LUAINFO_H__

#include "dll_export.h"
#include "include/lua.hpp"

#include <string>
#include <vector>
#include <boost/cstdint.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/preprocessor.hpp>

#ifndef LLDEBUG_MEMBER_NVP
#define LLDEBUG_MEMBER_NVP(name) boost::serialization::make_nvp(BOOST_STRINGIZE(name), BOOST_PP_CAT(m_, name))
#endif

namespace LuaDebug {

    class LuaContext;

    /**
    * @brief Guarantee the consistency of lua_State.
    */
    class scoped_lua
    {
    public:
        explicit scoped_lua(lua_State *L);
        explicit scoped_lua(LuaContext *ctx, lua_State *L, bool withDebug=false);
        ~scoped_lua();

        /// Check the stack top, and return n.
        int check(int n);

    private:
        void init(LuaContext *ctx, lua_State *L, bool withDebug);
        LuaContext *m_ctx;
        lua_State *m_L;
        int m_top;
        bool m_isOldEnabled;
    };

    /**
    * @brief Handle of the lua_State* object.
    */
    class _LUAVM_API LuaHandle {
    public:
        explicit LuaHandle()
            : m_luaState(0) {
        }

        LuaHandle(const LuaHandle &x)
            : m_luaState(x.m_luaState) {
        }

        ~LuaHandle() {
        }

        LuaHandle &operator =(const LuaHandle &x) {
            m_luaState = x.m_luaState;
            return *this;
        }

        LuaHandle(lua_State *L) {
            SetState(L);
        }

        LuaHandle &operator =(lua_State *x) {
            SetState(x);
            return *this;
        }

        /// Get lua_State* object.
        lua_State *GetState() const {
            return reinterpret_cast<lua_State *>(m_luaState);
        }

        /// Set lua_State* object.
        void SetState(lua_State *L) {
            m_luaState = reinterpret_cast<boost::uint64_t>(L);
        }

    public:
        friend bool operator==(const LuaHandle &x, const LuaHandle &y) {
            return (x.m_luaState == y.m_luaState);
        }
        friend bool operator!=(const LuaHandle &x, const LuaHandle &y) {
            return !(x == y);
        }
        friend bool operator <(const LuaHandle &x, const LuaHandle &y) {
            return (x.m_luaState < y.m_luaState);
        }
        friend bool operator >(const LuaHandle &x, const LuaHandle &y) {
            return (y < x);
        }
        friend bool operator<=(const LuaHandle &x, const LuaHandle &y) {
            return !(x > y);
        }
        friend bool operator>=(const LuaHandle &x, const LuaHandle &y) {
            return !(x < y);
        }

    private:
        friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive& ar, const unsigned int) {
            ar & LLDEBUG_MEMBER_NVP(luaState);
        }

    private:
        boost::uint64_t m_luaState;
    };

    /**
    * @brief Stackframe object.
    */
    class _LUAVM_API LuaStackFrame {
    public:
        explicit LuaStackFrame(const LuaHandle &lua = LuaHandle(), int level = 0);
        ~LuaStackFrame();

        /// Get the lua state object.
        LuaHandle &GetLua() {
            return m_lua;
        }

        /// Get the const lua state object.
        const LuaHandle &GetLua() const {
            return m_lua;
        }

        /// Get the level of the local stack frame.
        int GetLevel() const {
            return m_level;
        }

    private:
        friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive& ar, const unsigned int) {
            ar & LLDEBUG_MEMBER_NVP(lua);
            ar & LLDEBUG_MEMBER_NVP(level);
        }

    private:
        LuaHandle m_lua;
        int m_level;
    };


    /**
    * @brief Infomation of a lua variable.
    */
    class _LUAVM_API LuaVar
    {
    public:
        LuaVar(const LuaHandle &lua, const std::string &name, int valueIdx);
        LuaVar(const LuaHandle &lua, const std::string &name, const std::string &error);

        explicit LuaVar();
        virtual ~LuaVar();

        /// Push the table value.
        int PushTable(lua_State *L) const;

        /// Is this object valid ?
        bool IsOk() const;

        /// Get the lua handle.
        const LuaHandle &GetLua() const;

        /// Get the name of the variable or table's key.
        const std::string &GetName() const;

        /// Get the value by string.
        const std::string &GetValue() const;

        /// Get the type by integer.
        int GetValueType() const;

        /// Get the type name.
        std::string GetValueTypeName() const;

        /// Does this have any fields (metatable of child elements) ?
        bool HasFields() const;

    protected:
        /// Check whether the variable has fields.
        bool CheckHasFields(lua_State *L, int valueIdx) const;

        /// Register and save valueIdx to the internal table.
        int RegisterTable(lua_State *L, int valueIdx);

    private:
        friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive& ar, const unsigned int) {
            ar & LLDEBUG_MEMBER_NVP(lua);
            ar & LLDEBUG_MEMBER_NVP(name);
            ar & LLDEBUG_MEMBER_NVP(value);
            ar & LLDEBUG_MEMBER_NVP(valueType);
            ar & LLDEBUG_MEMBER_NVP(tableIdx);
            ar & LLDEBUG_MEMBER_NVP(hasFields);
        }

    private:
        LuaHandle m_lua;
        std::string m_name;
        std::string m_value;
        int m_valueType;
        int m_tableIdx;
        bool m_hasFields;
    };


    /**
    * @brief Infomation of the function call.
    */
    class _LUAVM_API LuaBacktrace
    {
    public:
        explicit LuaBacktrace(const LuaHandle &lua,
            const std::string &name,
            const std::string &sourceKey,
            const std::string &sourceTitle,
            int line, int level);
        explicit LuaBacktrace();
        ~LuaBacktrace();

        /// Get the lua handle.
        const LuaHandle &GetLua() const;

        /// Get the function name that calls from.
        const std::string &GetFuncName() const;

        /// Get the source key.
        const std::string &GetKey() const;

        /// Get the source title.
        const std::string &GetTitle() const;

        /// Get the line number.
        int GetLine() const;

        /// Get the local stack level.
        int GetLevel() const;

    private:
        friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive& ar, const unsigned int) {
            ar & LLDEBUG_MEMBER_NVP(lua);
            ar & LLDEBUG_MEMBER_NVP(funcName);
            ar & LLDEBUG_MEMBER_NVP(key);
            ar & LLDEBUG_MEMBER_NVP(sourceTitle);
            ar & LLDEBUG_MEMBER_NVP(line);
            ar & LLDEBUG_MEMBER_NVP(level);
        }

    public:
        LuaHandle m_lua;
        std::string m_funcName;
        std::string m_key;
        std::string m_sourceTitle;
        int m_line;
        int m_level;
    };

    typedef std::vector<LuaVar>       LuaVarList;
    typedef std::vector<LuaVarList>   LuaMultiVarList;
    typedef std::vector<LuaBacktrace> LuaBacktraceList;


} // end of namespace LuaDebug

#endif
